#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

//#include <memory>

#include "IniRw.h"




char *trimLeft( char *,const char* seps);
char *trimRight( char *,const char* seps );
char *trim( char *,const char* seps );
vector<char*> split(char *,const char* seps);
string getValString(IniData data,const string section,const string key);
bool containchar(const char inChar,const char *srcstr);

void removeExplain(char *srcStr)
{
    if (!srcStr) return;
    for (size_t i = 0; i < strlen(srcStr); i++)
    {
        if ( srcStr[i]=='#' )
        {
            srcStr[i] = '\0';
            break;
        }
    }
    
}

IniRw::IniRw()
    :fd(0)
{

}

IniRw::IniRw(const string iniFile_)
    :fd(0)
    ,iniFile(iniFile_)
{
    fd = fopen(iniFile.c_str(),"r");
    if (!fd)
    {
      
        return;
    }
    IniPrivateData tmpData;
    while (!feof(fd)) 
    { 
        char strLine[1024];
        memset(strLine,0,sizeof(strLine));
        fgets(strLine,1024,fd);  //读取一行
    //    printf("%s\n", strLine); //输出
        trimLeft(strLine," \t");
        trimRight(strLine,"\n\r \t");
        if (strlen(strLine) >0 && strLine[0]!='#')
        {
            if (strLine[0]=='[' && strLine[strlen(strLine)-1]==']')
            {
                 if (!tmpData.section.empty())
                 {
                     data.push_back(tmpData);
                 }
                tmpData.keyData.clear();
                trimLeft(strLine,"[");
                trimRight(strLine,"]");
                trim(strLine," ");
                tmpData.section = strLine;
            }else
            {
                vector<char *> keys = split(strLine,"=");
                if (keys.size()==2)
                {
                    trim(keys[0]," \t");
                    removeExplain(keys[1]);
                    trim(keys[1]," \t");
                    trim(keys[1],"\"");
                    tmpData.keyData.erase(keys[0]);
                    tmpData.keyData.insert(make_pair(keys[0],keys[1]));

                }
            }
            
        }
    }

    if (!tmpData.section.empty()){
        data.push_back(tmpData);
    }
    fclose(fd);
    fd  = 0;
}

IniRw::~IniRw()
{
    if (fd)
        fclose(fd);
    fd = 0;
}

int IniRw::Read(const string section,const string key,const int defaultval)
{
    int tmpInt = defaultval;
    string val = getValString(data,section,key);
    if (!val.empty())
    {
        try
        {
            tmpInt = atoi(val.c_str());
        }
        catch(const std::exception& e)
        {
            printf("atoi error.src:%s,error:%s",val.c_str(),e.what());
        }
    }
    return tmpInt;
}

string IniRw::Read(const string section,const string key,const string defaultval)
{
    string tmpVal = defaultval;
    string val = getValString(data,section,key);
    if (!val.empty())
        tmpVal = val;
    return tmpVal;
}

bool IniRw::Save()
{
     return SaveAs(iniFile);
}

bool IniRw::SaveAs(const string otherIniFile)
{
    fd = fopen(otherIniFile.c_str(),"w+");
    if (!fd) return false;
    for (IniData::iterator it = data.begin();it!=data.end();it++)
    {
        char tmpStr[1024];
        sprintf(tmpStr,"[%s]\n",it->section.c_str());
        fputs(tmpStr,fd);
        for (map<string,string>::iterator mapIt = it->keyData.begin(); mapIt!=it->keyData.end(); mapIt++)
        {
            sprintf(tmpStr,"%s = %s\n",mapIt->first.c_str(),mapIt->second.c_str());
            fputs(tmpStr,fd);
        }
        
    }
    fflush(fd);
    fclose(fd);
    fd = 0;
    return true;
}

void IniRw::writeInt(const string section,const string key,const int val)
{
    char tmpstr[1024];
    sprintf(tmpstr,"%u",(uint32_t)val);
    writeString(section,key,tmpstr);
}

void IniRw::writeString(const string section,const string key,const string val)
{
    IniData::iterator it;
    for (it=data.begin();it!=data.end();it++)
    {
        if (strcmp( it->section.c_str(),section.c_str())==0)
        {
            it->keyData.erase(key);
            break;
        }
    }

    if (it!=data.end())
    {
        it->keyData.insert(make_pair(key,val));
    }else
    {
        IniPrivateData tmpData;
        tmpData.section = section;
        tmpData.keyData.insert(make_pair(key,val));
        data.push_back(tmpData);
    }
    
}

char *trimLeft( char *src,const char* seps)
{
    if(!src) return src;
    size_t pos = 0;
    for (; pos < strlen(src); pos++)
    {
        if (!containchar(src[pos],seps))
        {
            break;
        }
    }
    if (pos>0)
    {
        char tmpStr[1024];
        strcpy(tmpStr,src+pos);
        strcpy(src,tmpStr);
    }

    return src;
}

char *trimRight( char *src,const char* seps )
{
    if(!src) return src;
    int pos =  (int)strlen(src)-1;
    for (; pos>=0 ; pos--)
    {
        if (containchar(src[pos],seps)){
            src[pos]='\0';
        }else
        {
            break;
        }
        
    }
    return src;
}

vector<char *> split(char *s,const char* seps)
{
    vector<char *> vs;
    char *resStr = NULL;
    resStr = strtok(s,seps);
    while (resStr)
    {
        vs.push_back(resStr);
        resStr = strtok(NULL,seps);
    }
    return vs;
}

char *trim( char *src,const char* seps )
{
    trimLeft(src,seps);
    trimRight(src,seps);
    return src;
}

string getValString(IniData data, const string section,const string key)
{
    IniData::iterator it;
    for (it=data.begin();it!=data.end();it++)
    {
        if (strcmp( it->section.c_str(),section.c_str())==0)
        {
            map<string,string>::iterator keyIt =  it->keyData.find(key);
            if (keyIt!=it->keyData.end())
            {
                return keyIt->second;
            }
            
        }
    }
    return string();
}

bool containchar(const char inChar,const char *srcstr)
{
    if (!srcstr) return false;
    for (size_t i = 0; i < strlen(srcstr); i++)
    {
        if (inChar==srcstr[i])
        {
            return true;
        }
    }
    return false;
    
}

string IniRw::readString(const string filename,const string section,const string key,const string defaultval)
{
    IniRw inirw(filename);
    return inirw.Read(section,key,defaultval);
}

int IniRw::readInt(const string filename,const string section,const string key,const int defaultval)
{
    IniRw inirw(filename);
    return inirw.Read(section,key,defaultval);
}